﻿using System;
using System.Collections.Generic;
using System.Collections;

namespace Aeon.Framework
{

/// <summary>
/// Selective Singleton pattern for AeonTexture.
/// Works in a single context.
/// 
/// </summary>
public sealed class AeonTextureManager : ITextureManager
    {
    // --- Attributes:

    private Dictionary<TextureCore,UInt16> textures;    // Created textures list, and use count.
    //private Array<TextureCore> textures;
    //private Array<UInt16> counter;
    
    // --- Constructor:

    /// <summary>
    /// Internal constructor for world instatiation.
    /// 
    /// </summary>
    public AeonTextureManager()
        {
        
        textures = new Dictionary<TextureCore,UInt16>();
        }

    // --- Internal Methods:

    /// <summary>
    /// Removes a texture decreasing the use counter for the core.
    /// Possibly removes the core from the cache.
    /// 
    /// </summary>
    private void removeCore(TextureCore core)
        {
        bool orphan = isOrphan(core);

        if (orphan)
           {
           core.Dispose();
           textures.Remove(core);
           }

        else { textures[core]--; }
        }

    /// <summary>
    /// Finds if a core to be removed is used by another texture.
    /// 
    /// </summary>
    private bool isOrphan(TextureCore core)
        {
        ushort users = textures[core];
        bool orphan = (users>1) ? (false):(true);

        return(orphan);
        }

    // --- Public Methods:

    /// <summary>
    /// Stops managing texture, possibly unloading it if unused.
    /// 
    /// </summary>
    public void ForgetTexture(ITexture sender)
        {

        removeCore(sender.Core);
        }

    /// <summary>
    /// Creates or reuses textures in memory, cleaning orphaned data.
    /// 
    /// </summary>
    public void TextureChanged(ITexture sender)
        {
        // If names are different, a texture is swapping cores.
        // Also, new empty textures have no core on cache.
        // Remove old core.
        
        if (sender.Name != sender.Core.Path && !sender.Core.Empty) { removeCore(sender.Core); }

        // Load core.
        // Reuse existing core and increase counter.
        // Or create a new core and set counter.

        TextureCore reuse = null;
        foreach(TextureCore c in textures.Keys)
            {
            if (c.Path == sender.Name) 
                {
                reuse = c;
                break;
                }
            }

        if (reuse != null)
            {
            sender.Core = reuse;
            textures[reuse]++;
            }

        else {
             TextureCore core = new TextureCore(sender.Name);
             textures.Add(core,1);
             sender.Core = core;
             }
        }
    
    /// <summary>
    /// Cleans all OpenGl context data.
    /// 
    /// </summary>
    public void CleanTextures()
        {
        foreach(TextureCore c in textures.Keys) { c.Dispose(); }

        textures.Clear();
        }

    }


}
